package com.jiujichaoshi.oauth.server.security.verify;

import com.jiujichaoshi.oauth.server.security.CustomContextAware;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.AsyncHandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.Optional;

/**
 * Description: <p>用于处理被Verify注解的请求，从请求参数获取人机验证相关的参数，并进行验证</p>
 * preHandle返回true，继续执行下一个拦截器。false，结束请求。
 * <p>
 * 实现人机校验这是方法二，使用interceptor的方式。
 * 使用filter的方式见{@link com.jiujichaoshi.oauth.server.security.AbstractDdosDefenderFilter}
 *
 * @author YangLong [410357434@163.com]
 * @version V1.0
 * @date 2021/7/6
 */
@Slf4j
@Component
public class VerifyHandlerInterceptor implements AsyncHandlerInterceptor {
    @Autowired
    private CustomContextAware customContextAware;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.debug("intercept the request:{}", request.getRequestURI());
        Optional<Verify> needVerify = this.getAuthCheck(handler);
        //不需要人机验证
        if (!needVerify.isPresent()) {
            return true;
        }
        //根据注解的类型，获取对应的实现类，然后进行验证，通过返回true，未通过抛出异常或通过response进行返回并返回false
        Verify annotation = needVerify.get();
        VerifyType type = annotation.value();
        VerifyService verifyService = customContextAware.getBean(type.getImpl());
        if (verifyService.pass(request.getParameterMap())) {
            //继续执行业务
            return true;
        } else {
            response.setStatus(HttpStatus.OK.value());
            PrintWriter writer = response.getWriter();
            //生成json字符串返回给前端
            writer.write("{'message':'未通过人机验证','status':'300'}");
            writer.flush();
        }
        return false;
    }

    /**
     * 判断HandlerMethod有没有被Verify注解
     *
     * @param handler HandlerMethod
     * @return Optional
     */
    private Optional<Verify> getAuthCheck(Object handler) {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Verify verify = handlerMethod.getMethod().getAnnotation(Verify.class);
            if (null == verify) {
                return Optional.empty();
            }
            return Optional.of(verify);
        }
        return Optional.empty();
    }
}
